package gobase

import (
	"io"
	"sort"
	"sync"
	"sync/atomic"
)

type StatuGetter interface {
	// 获取当前状态
	GetStatus(args ...interface{}) string
}

// 动作执行
type ActionExecutor interface {
	ExecuteAction(action int, args ...interface{}) (r interface{}, err error)
}

type CheckEngineStarter interface {
	CheckStart() error
}

type ICheckStart interface {
	CheckStart() error
}

type ObjectInnerRef struct {
	refN int32

	owner   *ConnectObjectRefStorage
	connstr string
	inner   interface{}
}

func (this *ObjectInnerRef) GetObject() interface{} {
	return this.inner
}

func (this *ObjectInnerRef) CheckStart() error {
	if lvintf, ok := this.inner.(CheckEngineStarter); ok {
		return lvintf.CheckStart()
	}
	return ErrUnsupported
}

func (this *ObjectInnerRef) ExecuteAction(action int, args ...interface{}) (r interface{}, err error) {
	if lvintf, ok := this.inner.(ActionExecutor); ok {
		return lvintf.ExecuteAction(action, args...)
	}
	return nil, ErrUnsupported
}

func (this *ObjectInnerRef) GetStatus(args ...interface{}) string {
	if lvintf, ok := this.inner.(StatuGetter); ok {
		return lvintf.GetStatus(args...)
	}
	return ""
}

func (this *ObjectInnerRef) ReleaseRef() int {
	n := atomic.AddInt32(&this.refN, -1)
	if n == 0 {
		delflag := 0
		owner := this.owner
		owner.lk.Lock()
		if atomic.LoadInt32(&this.refN) == 0 {
			delete(owner.lst, this.connstr)
			delflag = 1
		}
		owner.lk.Unlock()
		if delflag == 1 {
			obj := this.inner
			this.inner = nil
			if intf, ok := obj.(io.Closer); ok {
				go intf.Close()
			}
			return 1
		}
	}
	return 0
}

// 连接对象管理,
// 借出去对象被+1, 归还-1 =0:释放
type ConnectObjectRefStorage struct {
	lk      sync.RWMutex
	lst     map[string]*ObjectInnerRef
	newfunc func(connstr string) interface{}
}

func NewConnectObjectRefStorage(newfun func(connstr string) interface{}) *ConnectObjectRefStorage {
	rval := &ConnectObjectRefStorage{
		newfunc: newfun,
		lst:     make(map[string]*ObjectInnerRef),
	}
	return rval
}

func (this *ConnectObjectRefStorage) Range(filter func(obj interface{}) bool, lessfn func(iobj, jobj interface{}) bool, fn func(obj interface{}) bool) {
	var lst []interface{}
	this.lk.RLock()
	for _, v := range this.lst {
		vobj := v.GetObject()
		if filter != nil {
			if filter(vobj) {
				lst = append(lst, vobj)
			}
		} else {
			lst = append(lst, vobj)
		}
	}
	this.lk.RUnlock()

	if lessfn != nil {
		sort.Slice(lst, func(i, j int) bool {
			return lessfn(lst[i], lst[j])
		})
	}

	if fn != nil {
		for i := 0; i < len(lst); i++ {
			v := lst[i]
			if !fn(v) {
				break
			}
		}
	}
}

func (this *ConnectObjectRefStorage) GetStatus(args ...interface{}) string {
	var sb BytesBuilder
	var lst []*ObjectInnerRef
	this.lk.RLock()
	for _, v := range this.lst {
		lst = append(lst, v)
	}
	this.lk.RUnlock()

	sort.Slice(lst, func(i, j int) bool {
		return lst[i].connstr < lst[j].connstr
	})

	for i := 0; i < len(lst); i++ {
		v := lst[i]
		if lvintf, ok := v.inner.(StatuGetter); ok {
			sb.Appendf("connstr:%s\nref:%d\n:status:%s\n\n", v.connstr, v.refN, lvintf.GetStatus(args...))
		} else {
			sb.Appendf("connstr:%s\nref:%d\n\n", v.connstr, v.refN)
		}
	}

	return sb.String()
}

func (this *ConnectObjectRefStorage) CheckGetRef(connstr string) *ObjectInnerRef {
	this.lk.RLock()
	itm := this.lst[connstr]
	if itm != nil {
		atomic.AddInt32(&itm.refN, 1)
	}
	this.lk.RUnlock()
	if itm == nil {
		this.lk.Lock()
		itm = this.lst[connstr]
		if itm == nil {
			itm = &ObjectInnerRef{
				connstr: connstr,
				owner:   this,
				inner:   this.newfunc(connstr),
			}
			this.lst[connstr] = itm
		}
		atomic.AddInt32(&itm.refN, 1)
		this.lk.Unlock()
	}
	return itm
}

func (this *ConnectObjectRefStorage) ReleaseRef(itm *ObjectInnerRef) {
	n := atomic.AddInt32(&itm.refN, -1)
	if n == 0 {
		delflag := 0
		this.lk.Lock()
		if atomic.LoadInt32(&itm.refN) == 0 {
			delete(this.lst, itm.connstr)
			delflag = 1
		}
		this.lk.Unlock()
		if delflag == 1 {
			obj := itm.inner
			itm.inner = nil
			if intf, ok := obj.(io.Closer); ok {
				go intf.Close()
			}
		}
	}
}
